package baseutils

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"gitee.com/ichub/goconfig/common/base/baseconsts"
	"gitee.com/ichub/goconfig/common/base/baseiface"
	"github.com/GUAIK-ORG/go-snowflake/snowflake"
	"github.com/duke-git/lancet/fileutil"
	"github.com/gogf/gf/crypto/gdes"
	"github.com/gogf/gf/util/gconv"
	"github.com/gookit/goutil/strutil"
	"github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"
	"gopkg.in/ini.v1"
	"log"
	"math"
	"math/big"
	"net"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

// 获取uuid
func Uuid() string {
	u4, _ := uuid.NewV4()

	return strings.ReplaceAll(u4.String(), "-", "")

}

// check inst impl interface T
func IfIface[T any](inst any) bool {

	var ifaceType T
	return reflect.TypeOf(inst).Implements(reflect.TypeOf(ifaceType).Elem())

}

func SnowflakeNextVal() int64 {
	var sf, _ = snowflake.NewSnowflake(int64(0), 0)

	return sf.NextVal()

}
func Any2Str(value interface{}) string {
	if value == nil {
		return ""
	}

	v, _ := strutil.String(value)
	return v
}

func FmtPrintLn(a ...any) {
	var now = time.Now().String()

	fmt.Println(now, a)
}

func NameOfType(v any) string {
	var modelName = reflect.TypeOf(v)
	var mName = strings.ReplaceAll(modelName.String(), "\\*", "")
	mName = strings.ReplaceAll(modelName.String(), ".", "")

	mName = strings.TrimPrefix(mName, "*")
	logrus.Info("NameOfType=", mName)
	return strutil.SnakeCase(mName)
}
func DesEnc(c []byte, key []byte) ([]byte, error) {
	if len(key) != 8 {
		return []byte{}, errors.New("key length invalid!")
	}
	return gdes.EncryptECB(c, key, gdes.PKCS5PADDING)
}

// ..	var tt, rrr = gdes.DecryptECB(r, []byte("12345678"), gdes.PKCS5PADDING)
func DesDec(c []byte, key []byte) ([]byte, error) {
	if len(key) != 8 {
		return []byte{}, errors.New("key length invalid!")
	}
	return gdes.DecryptECB(c, key, gdes.PKCS5PADDING)
}
func MapKey2Str(mapkey map[string]interface{}) strings.Builder {
	var builder = strings.Builder{}
	builder.WriteString("\r\n")
	var i = 1
	for k, _ := range mapkey {
		var inf = fmt.Sprintf("funcs %03d : %s\r\n", i, k)
		builder.WriteString(inf)
		i++
	}
	return builder

}
func Map2Str(mapkey map[string]interface{}) strings.Builder {
	var builder = strings.Builder{}
	builder.WriteString("\r\n")
	var i = 1
	for k, v := range mapkey {
		var inf = fmt.Sprintf("funcs %03d  %s: value  %s\r\n", i, k, v)
		builder.WriteString(inf)
		i++
	}
	return builder

}
func FormatNow() string {
	now := time.Now()

	return FormatDatetime(now)
}

func FormatDatetime(t time.Time) string {
	//now := time.Now()

	return t.Format(baseconsts.FormatDateTime)
}
func CheckType(i interface{}) string {
	switch i.(type) {
	case string:
		return "string"
	case bool:
		return "bool"
	case int:
		return "int"
	case int8:
		return "int8"
	case int16:
		return "int16"
	case int32:
		return "int32"
	case int64:
		return "int64"
	case byte:
		return "byte"
	case float32:
		return "float32"
	case float64:
		return "float64"

	case *string:
		return "*string"
	case *bool:
		return "*bool"
	case *int:
		return "*int"
	case *int8:
		return "*int8"
	case *int16:
		return "*int16"
	case *int32:
		return "*int32"
	case *int64:
		return "*int64"
	case *byte:
		return "*byte"
	case *float32:
		return "*float32"
	case *float64:
		return "*float64"
	}

	return "none"
}
func NewIchubLogger(pathfile string) *log.Logger {
	dir := path.Dir(pathfile)

	if !fileutil.IsExist(dir) {
		os.MkdirAll(dir, os.ModePerm)

	}
	fileutil.RemoveFile(pathfile)

	return NewIchubLog(pathfile)

}

func NewIchubLog(filename string) (logger *log.Logger) {
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
	if err != nil {
		log.Fatalln(err)
	}
	logger = log.New(file, "", log.LstdFlags|log.Llongfile)
	return
}

func LoadIniCfg(fileName string) (*ini.File, error) {

	cfg, err := ini.Load(fileName) //opt.ConfigFileName)
	return cfg, err
}

// https://www.jianshu.com/p/982c4fabb11d swagg参数
func Nipv4() string {
	ifaces, err := net.Interfaces()
	if err != nil {
		fmt.Println(err)
		return ""
	}

	for _, iface := range ifaces {
		addrs, err := iface.Addrs()
		if err != nil {
			logrus.Error(err.Error())
			continue
		}
		for _, addr := range addrs {
			ipnet, ok := addr.(*net.IPNet)
			if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil {
				ip := ipnet.IP
				return ip.String()
			}
		}
	}
	return ""
}

// StrTo is the target string
type StrTo string

// Set string
func (f *StrTo) Set(v string) {
	if v != "" {
		*f = StrTo(v)
	} else {
		f.Clear()
	}
}

// Clear string
func (f *StrTo) Clear() {
	*f = StrTo(0x1E)
}

// Exist check string exist
func (f StrTo) Exist() bool {
	return string(f) != string(0x1E)
}

// Bool string to bool
func (f StrTo) Bool() (bool, error) {
	return strconv.ParseBool(f.String())
}

// Float32 string to float32
func (f StrTo) Float32() (float32, error) {
	v, err := strconv.ParseFloat(f.String(), 32)
	return float32(v), err
}

// Float64 string to float64
func (f StrTo) Float64() (float64, error) {
	return strconv.ParseFloat(f.String(), 64)
}

// Int string to int
func (f StrTo) Int() (int, error) {
	v, err := strconv.ParseInt(f.String(), 10, 32)
	return int(v), err
}

// Int8 string to int8
func (f StrTo) Int8() (int8, error) {
	v, err := strconv.ParseInt(f.String(), 10, 8)
	return int8(v), err
}

// Int16 string to int16
func (f StrTo) Int16() (int16, error) {
	v, err := strconv.ParseInt(f.String(), 10, 16)
	return int16(v), err
}

// Int32 string to int32
func (f StrTo) Int32() (int32, error) {
	v, err := strconv.ParseInt(f.String(), 10, 32)
	return int32(v), err
}

// Int64 string to int64
func (f StrTo) Int64() (int64, error) {
	v, err := strconv.ParseInt(f.String(), 10, 64)
	if err != nil {
		i := new(big.Int)
		ni, ok := i.SetString(f.String(), 10) // octal
		if !ok {
			return v, err
		}
		return ni.Int64(), nil
	}
	return v, err
}

// Uint string to uint
func (f StrTo) Uint() (uint, error) {
	v, err := strconv.ParseUint(f.String(), 10, 32)
	return uint(v), err
}

// Uint8 string to uint8
func (f StrTo) Uint8() (uint8, error) {
	v, err := strconv.ParseUint(f.String(), 10, 8)
	return uint8(v), err
}

// Uint16 string to uint16
func (f StrTo) Uint16() (uint16, error) {
	v, err := strconv.ParseUint(f.String(), 10, 16)
	return uint16(v), err
}

// Uint32 string to uint31
func (f StrTo) Uint32() (uint32, error) {
	v, err := strconv.ParseUint(f.String(), 10, 32)
	return uint32(v), err
}

// Uint64 string to uint64
func (f StrTo) Uint64() (uint64, error) {
	v, err := strconv.ParseUint(f.String(), 10, 64)
	if err != nil {
		i := new(big.Int)
		ni, ok := i.SetString(f.String(), 10)
		if !ok {
			return v, err
		}
		return ni.Uint64(), nil
	}
	return v, err
}
func ContainsType(s interface{}, name string) bool {
	value := reflect.ValueOf(s)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	if value.Kind() != reflect.Struct {
		return false
	}

	for i := 0; i < value.NumField(); i++ {
		//	logrus.Info(value.Field(i).Type().Name())
		if value.Field(i).Type().Name() == name {
			return true
		}
	}

	return false
}
func IfBaseEntity(Some any) bool {

	return ContainsType(Some, "BaseEntity") ||
		ContainsType(Some, "BaseEntitySingle") ||
		ContainsTypeOfSub(Some, "BaseEntity") ||
		ContainsTypeOfSub(Some, "BaseEntitySingle")

}

func ContainsTypeOfSub(s interface{}, name string) bool {
	value := reflect.ValueOf(s)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	if value.Kind() != reflect.Struct {
		return false
	}

	for i := 0; i < value.NumField(); i++ {
		logrus.Info(value.Field(i).Type().Name())
		if value.Field(i).Kind() == reflect.Struct {

			return ContainsType(value.Field(i).Interface(), name)
		}

	}

	return false
}

// String string to string
func (f StrTo) String() string {
	if f.Exist() {
		return string(f)
	}
	return ""
}

// ToStr interface to string
func ToStr(value interface{}, args ...int) (s string) {
	switch v := value.(type) {
	case bool:
		s = strconv.FormatBool(v)
	case float32:
		s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
	case float64:
		s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
	case int:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int8:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int16:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int32:
		s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
	case int64:
		s = strconv.FormatInt(v, argInt(args).Get(0, 10))
	case uint:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint8:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint16:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint32:
		s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
	case uint64:
		s = strconv.FormatUint(v, argInt(args).Get(0, 10))
	case string:
		s = v
	case []byte:
		s = string(v)
	default:
		s = fmt.Sprintf("%v", v)
	}
	return s
}

// ToInt64 interface to int64
func ToInt64(value interface{}) (d int64) {
	val := reflect.ValueOf(value)
	switch value.(type) {
	case int, int8, int16, int32, int64:
		d = val.Int()
	case uint, uint8, uint16, uint32, uint64:
		d = int64(val.Uint())
	default:
		panic(fmt.Errorf("ToInt64 need numeric not `%T`", value))
	}
	return
}
func ToInt32(value interface{}) (d int32) {
	val := reflect.ValueOf(value)
	switch value.(type) {
	case int, int8, int16, int32, int64:
		d = int32(val.Int())
	case uint, uint8, uint16, uint32, uint64:
		d = int32(val.Uint())
	default:
		panic(fmt.Errorf("ToInt64 need numeric not `%T`", value))
	}
	return
}

// snake string, XxYy to xx_yy , XxYY to xx_yy
func snakeString(s string) string {
	data := make([]byte, 0, len(s)*2)
	j := false
	num := len(s)
	for i := 0; i < num; i++ {
		d := s[i]
		if i > 0 && d >= 'A' && d <= 'Z' && j {
			data = append(data, '_')
		}
		if d != '_' {
			j = true
		}
		data = append(data, d)
	}
	return strings.ToLower(string(data[:]))
}

// camel string, xx_yy to XxYy
func camelString(s string) string {
	data := make([]byte, 0, len(s))
	flag, num := true, len(s)-1
	for i := 0; i <= num; i++ {
		d := s[i]
		if d == '_' {
			flag = true
			continue
		} else if flag {
			if d >= 'a' && d <= 'z' {
				d = d - 32
			}
			flag = false
		}
		data = append(data, d)
	}
	return string(data[:])
}

type argString []string

// get string by index from string slice
func (a argString) Get(i int, args ...string) (r string) {
	if i >= 0 && i < len(a) {
		r = a[i]
	} else if len(args) > 0 {
		r = args[0]
	}
	return
}

type argInt []int

// get int by index from int slice
func (a argInt) Get(i int, args ...int) (r int) {
	if i >= 0 && i < len(a) {
		r = a[i]
	}
	if len(args) > 0 {
		r = args[0]
	}
	return
}

// parse time to string with location
func timeParse(dateString, format string) (time.Time, error) {
	tp, err := time.ParseInLocation(format, dateString, time.Local)
	return tp, err
}

// get pointer indirect type
func indirectType(v reflect.Type) reflect.Type {
	switch v.Kind() {
	case reflect.Ptr:
		return indirectType(v.Elem())
	default:
		return v
	}
}

func SetFieldValue(val reflect.Value, value interface{}) {
	switch val.Kind() {
	case reflect.Bool:

		val.SetBool(gconv.Bool(value))

	case reflect.String:

		val.SetString(gconv.String(value))

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

		val.SetInt(gconv.Int64(value))

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		val.SetUint(gconv.Uint64(value))
	case reflect.Float64, reflect.Float32:

		val.SetFloat(gconv.Float64(value))

	case reflect.Struct:
		if value == nil {
			val.Set(reflect.Zero(val.Type()))

		} else if _, ok := val.Interface().(time.Time); ok {
			var str string
			switch d := value.(type) {
			case time.Time:
				val.Set(reflect.ValueOf(d))
			case []byte:
				str = string(d)
			case string:
				str = d
			}
			if str != "" {
				if len(str) >= 19 {
					str = str[:19]
					t, err := time.ParseInLocation(baseconsts.FormatDateTime, str, time.Local)
					if err == nil {
						t = t.In(time.Local)
						val.Set(reflect.ValueOf(t))
					}
				} else if len(str) >= 10 {
					str = str[:10]
					t, err := time.ParseInLocation(baseconsts.FormatDate, str, time.Local)
					if err == nil {
						val.Set(reflect.ValueOf(t))
					}
				}
			}
		}
	}
}

var MbTable = []uint16{
	0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
	0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
	0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
	0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
	0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
	0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
	0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
	0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
	0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
	0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
	0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
	0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
	0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
	0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
	0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
	0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
	0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
	0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
	0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
	0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
	0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
	0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
	0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
	0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
	0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
	0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
	0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
	0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
	0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
	0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
	0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
	0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040}

func ModbusCrcCheckSum(data []byte) []byte {
	var crc16 uint16
	crc16 = 0xffff
	for _, v := range data {
		n := uint8(uint16(v) ^ crc16)
		crc16 >>= 8
		crc16 ^= MbTable[n]
	}

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.LittleEndian, crc16)

	return bytesBuffer.Bytes()
}

// Modbus协议Crc校验
func CheckCrc(cmd []byte, data []byte, pos int, dlen int) bool {
	readlen := len(data)

	//截取追加字符
	if int(data[pos])+dlen < readlen {
		readlen = int(data[2]) + dlen
	}
	if cmd[0] != data[0] || cmd[1] != data[1] {
		return false
	} else {
		crc := ModbusCrcCheckSum(data[0 : readlen-2])
		if crc[0] != data[readlen-2] || crc[1] != data[readlen-1] {
			return false
		}
	}

	return true
}

var sequ_index uint32 = 0

func UniqueId() string {
	sequ_index = atomic.AddUint32(&sequ_index, 1)
	time.Now().UnixNano()
	return fmt.Sprintf("%x%04x", time.Now().Unix(), sequ_index)
}

// 获取时间
func GetTime() int64 {
	cur := time.Now()
	timestamp := strconv.FormatInt(cur.UnixNano(), 10)
	re := ToStr(timestamp[:13])
	timeint, _ := strconv.ParseInt(re, 10, 64)
	return timeint
}

func Round(f float64, n int) float64 {
	n10 := math.Pow10(n)
	return math.Trunc((f+0.5/n10)*n10) / n10
}

func sec2millsec(sec int64) int64 {
	return sec * 1000
}

func FindNameOfIface(iface any) string {

	// 使用reflect.TypeOf获取接口的动态类型
	ifaceType := reflect.TypeOf(iface)

	// 检查是否是指针类型
	if ifaceType.Kind() == reflect.Ptr {
		// 取指针指向的类型
		ifaceType = ifaceType.Elem()
	}
	// 获取类型名称
	return ifaceType.Name()

}
func IfProxy(some any) bool {
	if proxy, ok := some.(baseiface.IbaseProxy); ok {
		proxy.InitProxy(proxy)
		return ok
	}
	return false

}

// T is *type
func NewOfPtrType[T baseiface.IpoolObj]() T {
	var ptr T
	var objType = reflect.TypeOf(ptr)
	if objType.Kind() != reflect.Ptr {
		return NewOfType[T]()
	}
	var value = reflect.New(objType.Elem())
	var res = value.Interface().(T)
	res.Init()
	IfProxy(res)
	return res

}
func NewOfType[T baseiface.IpoolObj]() T {
	var ptr T
	ptr.Init()
	IfProxy(&ptr)
	return ptr

}
